﻿using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Xml.Linq;
using System.Reflection;

namespace Utils
{
    public abstract class InjectionFactory
    {
        private static readonly List<InjectionFactory> factories = new List<InjectionFactory>();

        private ConcurrentDictionary<Type, object> _instanceList = new ConcurrentDictionary<Type, object>();

        internal static readonly List<Type> _typeList = new List<Type>();

        public static List<Type> GetServiceTypes()
        {
            return _typeList;
        }

        public static void AddFactory(InjectionFactory injectionFactory)
        {
            if (!factories.Contains(injectionFactory))
            {
                factories.Add(injectionFactory);
            }
        }

        public static void AddFactory(Func<IServiceProvider, Type, object[], object> injectionFactory)
        {
            AddFactory(new DefaultInjectionFactory(injectionFactory));
        }
        /// <summary>
        /// 添加创建工程
        /// </summary>
        /// <param name="injectionFactory"></param>
        public static void AddFactory(Func<IServiceProvider, Type, object> injectionFactory)
        {
            AddFactory(new DefaultInjectionFactory((s, t, o) => injectionFactory(s, t)));
        }


        public object GetOrCreateInstance(IServiceProvider serviceProvider, Type implementationType, object[] attributes)
        {
            return _instanceList.GetOrAdd(implementationType, type => CreateInstance(serviceProvider, type, attributes));
        }

        public abstract object CreateInstance(IServiceProvider serviceProvider, Type implementationType, object[] attributes);

        private static object Instance(IServiceProvider serviceProvider, Type fieldType, object[] attributes)
        {
            foreach (var factory in factories)
            {
                var objService = factory.CreateInstance(serviceProvider, fieldType, attributes);
                if (objService != null)
                {
                    return objService;
                }
            }
            return null;
        }


        public static T GetInstanceFormFactory<T>(IServiceProvider serviceProvider, object[] attributes)
        {
            var service = GetInstanceFormFactory(serviceProvider, typeof(T), attributes);
            if (service == null) return default;
            return (T)service;
        }

        public static object GetInstanceFormFactory(IServiceProvider serviceProvider, Type type, object[] attributes)
        {

            var service = serviceProvider.GetService(type);
            if (service != null)
            {
                return service;
            }
            else
            {
                return Instance(serviceProvider, type, attributes);
            }
        }
        public static void ImportsFormFactory(IServiceProvider serviceProvider, object obj)
        {
            var fs = obj.GetType().GetFields().Where(a => a.IsInitOnly).ToArray();
            foreach (var fieldInfo in fs)
            {
                if (fieldInfo.GetValue(obj) == null)
                {
                    var service = GetInstanceFormFactory(serviceProvider, fieldInfo.FieldType, fieldInfo.GetCustomAttributes(true));
                    if (service != null)
                    {
                        fieldInfo.SetValue(obj, service);
                    }
                }
            }
        }
    }

    public class DefaultInjectionFactory : InjectionFactory
    {
        private readonly Func<IServiceProvider, Type, object[], object> func;

        public DefaultInjectionFactory(Func<IServiceProvider, Type, object[], object> func)
        {
            this.func = func;
        }

        public override object CreateInstance(IServiceProvider serviceProvider, Type implementationType, object[] attributes)
        {
            return func?.Invoke(serviceProvider, implementationType, attributes);
        }
    }


}
